﻿
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Markup;

using alib;
using alib.Reflection;
using alib.Debugging;
using alib.Enumerable;
using alib.Collections;
using alib.Observable;

using agree.schema;

namespace agree.composition
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class f_base : identity_node, IFunctor
	{
		protected f_base(IRtParent parent, Type[] _targs, MethodInfo _mib)
			: base(parent)
		{
			this.rgt = _targs;
			this.mib = _mib;
		}

		readonly Type[] rgt;
		public Type[] Targs { [DebuggerStepThrough] get { return rgt; } }

		readonly MethodInfo mib;

		public abstract Delegate Delegate { get; }

		public IFunctor Bind(IFunctor g)
		{
			Debug.Assert(rgt[0].IsAssignableFrom(g.Targs[1]));

			return (IFunctor)mib.MakeGenericMethod(g.Targs[0]).Invoke(this, new[] { g });
		}

		public override String ToString()
		{
			return String.Format("λ{0}", this.GetType()._Name());
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class f_base<T, U> : f_base, IFunctor<T, U>
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		static readonly MethodInfo mib;
		[DebuggerStepThrough]
		static f_base() { mib = typeof(f_base<T, U>).GetMethod("Bind", (BindingFlags)0x16); }

		protected f_base(IRtParent parent)
			: base(parent, TypeTuples<T, U>.Targs, mib)
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public abstract Func<T, U> Func { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public sealed override Delegate Delegate { [DebuggerStepThrough] get { return this.Func; } }

		public IFunctor<A, U> Bind<A>(IFunctor<A, T> gg)
		{
			return new f_bound<A, U>(this, this.Func, gg.Func);
		}

		public String ShortName
		{
			get { return typeof(f_base<T, U>)._Name(); }
			set { throw not.impl; }
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class f_primitive<T, U> : f_base<T, U>
	{
		public f_primitive(IRtParent parent)
			: base(parent)
		{
			this._f = func;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly Func<T, U> _f;

		protected abstract U func(T item);

		public sealed override Func<T, U> Func { [DebuggerStepThrough] get { return _f; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class f_impersonate_monad<T, U> : f_base<T, U>, IMonad
	{
		public f_impersonate_monad(IRtParent parent)
			: base(parent)
		{
		}
		public f_impersonate_monad(IRtParent parent, T arg)
			: base(parent)
		{
			this.t0 = arg;
		}
		protected readonly T t0;

		protected abstract U start(T item);

		public override Func<T, U> Func { get { return start; } }

		Object IMonad.ProvideValue(IServiceProvider sp) { return start(t0); }
		bool IMonad.IsCompleted { get { return true; } }
		void IMonad.Wait() { }
		int ρρInputItemsCount.InputItemsCount { get { return 0; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_bound<T, U> : f_primitive<T, U>
	{
		public f_bound(IRtParent parent, Delegate ff, Delegate gg)
			: base(parent)
		{
			this.mF = ff.Method;
			this.oF = ff.Target;
			this.mG = gg.Method;
			this.oG = gg.Target;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly MethodInfo mF, mG;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly Object oF, oG;

		[DebuggerStepThrough]
		protected override U func(T item)
		{
			return (U)mF.Invoke(oF, 0, null, new[] { mG.Invoke(oG, 0, null, new Object[] { item }, null) }, null);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class f_pred<T> : f_base<T, bool>
	{
		public f_pred(IRtParent parent, Func<T, bool> pred)
			: base(parent)
		{
			this.pred = pred;
		}
		readonly Func<T, bool> pred;

		public sealed override Func<T, bool> Func { get { return pred; } }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary> http://en.wikipedia.org/wiki/Unit_type </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_unit : identity_node, IUnit
	{
		public static readonly IUnit Instance = new f_unit(SysObj.Instance);
		f_unit(IRtParent parent) : base(parent) { }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class f_fatal<T, U> : f_primitive<T, U>
	{
		public static readonly f_fatal<T, U> Instance;
		static f_fatal() { Instance = new f_fatal<T, U>(); }
		protected f_fatal() : base(SysObj.Instance) { }
		protected sealed override U func(T _) { throw new Exception("fatal functor"); }
	};

	public sealed class f_fatal : f_fatal<ISchemaObj, ISchemaObj> { };

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_return<U> : f_primitive<IUnit, IActiveObj<U>>, IActiveObj<U>
		where U : IIdentity
	{
		public f_return(IRtParent parent, U u)
			: base(parent)
		{
			this.u = u;
		}
		readonly U u;
		public IDisposable Subscribe(IObserver<U> obs)
		{
			obs.OnNext(u);
			obs.OnCompleted();
			return EmptyDisposable.Instance;
		}

		protected sealed override IActiveObj<U> func(IUnit _) { return this; }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_factory<L, T, U> : f_primitive<T, U>, IAddArray<IIdentity>
		where T : IIdentity
		where L : class, IMonad<T, U>
		where U : class, IIdentity
	{
		public f_factory(IRtParent parent)
			: base(parent)
		{
			this.ci = find_ctor();
		}

		ConstructorInfo find_ctor()
		{
			ConstructorInfo ci;
			ParameterInfo[] rgpi;
			var rgci = typeof(L).GetConstructors();
			for (int i = 0; i < rgci.Length; i++)
				if ((rgpi = (ci = rgci[i]).GetParameters()).Length == 2 &&
					rgpi[0].ParameterType.IsAssignableFrom(this.GetType()) &&
					rgpi[1].ParameterType.IsAssignableFrom(typeof(T)))
					return ci;
			throw new Exception();
		}

		readonly ConstructorInfo ci;

		protected sealed override U func(T item)
		{
			var ldr = (L)ci.Invoke(0, null, new Object[] { this, item }, null);

			var pm = parent as xaml_functor_propagations;
			if (pm != null)
				pm.ApplySetters(ldr);

			U u = (U)ldr.ProvideValue(null);

			//var iar = u as IAsyncResult;
			//if (iar != null && !iar.IsCompleted)
			//	iar.AsyncWaitHandle.WaitOne();

			return u;
		}

		public int Count { get { return 1; } }

		public IIdentity[] ItemsArr { get { return new[] { this }; } set { throw not.valid; } }

		public IIdentity this[int index] { get { return this; } }

		IEnumerator<IIdentity> IEnumerable<IIdentity>.GetEnumerator() { yield return this; }
		IEnumerator IEnumerable.GetEnumerator() { yield return this; }
		void ICollection.CopyTo(Array array, int index) { array.SetValue(this, 0); }
		bool ICollection.IsSynchronized { get { return false; } }
		Object ICollection.SyncRoot { get { return this; } }
		void IAddArray<IIdentity>.Add(IIdentity _) { throw not.valid; }
	};


#if false
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	sealed class f_functor<F, T, U> : f_primitive<T, U>, IIdentity
		where F : class, IFunctor<T, U>
		where U : class, IIdentity
	{
		public f_functor(IIdentity prv)
			: base(prv)
		{
		}
		F functor;

		protected sealed override U func(T t)
		{
			if (functor == null)
			{
				functor = typeof(F)._ctor<F>(t);

				//var pm = functor as xaml_functor_propagations;
				//if (pm != null)
				//	pm.ApplySetters(ldr);
			}

			U u = functor.Func(t);

			var iar = u as IAsyncResult;
			if (iar != null && !iar.IsCompleted)
				iar.AsyncWaitHandle.WaitOne();

			return u;
		}
	};

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// the function which binds the specified functor to any given input functor
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_rebind : f_primitive<IFunctor, IFunctor>
	{
		public f_rebind(IRtParent parent, IFunctor f_rb)
			: base(prv)
		{
			this.cur = f_rb;
		}
		readonly IFunctor cur;

		protected sealed override IFunctor func(IFunctor prv)
		{
			return cur.Bind(prv);
		}
	};
#endif

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_factory_shared<M> : f_primitive<IUnit, M>
		where M : IIdentity
	{
		public f_factory_shared(IRtParent parent, M m)
			: base(parent)
		{
			this.m = m;
		}
		readonly M m;

		protected sealed override M func(IUnit _) { return m; }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_activate_seq<T> : f_primitive<IUnit, IActiveObj<T>>
		where T : IIdentity
	{
		public f_activate_seq(IRtParent parent, IIdentList<T> coll)
			: base(parent)
		{
			this.coll = coll;
		}
		readonly IIdentList<T> coll;

		protected sealed override IActiveObj<T> func(IUnit _)
		{
			return new _spooler(this, coll);
		}

		public sealed class _spooler : identity_node, IActiveObj<T>
		{
			public _spooler(IRtParent parent, IIdentList<T> coll)
				: base(parent, coll)
			{
				this.coll = coll;
			}
			readonly IIdentList<T> coll;
			public IDisposable Subscribe(IObserver<T> observer)
			{
				var e = coll.GetEnumerator();
				while (e.MoveNext())
					observer.OnNext(e.Current);
				observer.OnCompleted();
				return EmptyDisposable.Instance;
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_activate_ienum : f_primitive<IUnit, IActiveObj<IIdentity>>
	{
		public f_activate_ienum(IRtParent parent, IEnumerable<IIdentity> ee)
			: base(parent)
		{
			this.ee = ee;
		}
		readonly IEnumerable<IIdentity> ee;

		protected sealed override IActiveObj<IIdentity> func(IUnit _)
		{
			return typeof(_spooler<>)
				.MakeGenericType(f_compose.GetEnumerableArgType(ee.GetType()))
				._ctor<IActiveObj<IIdentity>>(this, ee);
		}

		public sealed class _spooler<T> : identity_node, IActiveObj<T>
			where T : IIdentity
		{
			public _spooler(IRtParent parent, IEnumerable<T> eee)
				: base(parent, eee as IIdentity)
			{
				this.eee = eee;
			}
			readonly IEnumerable<T> eee;
			public IDisposable Subscribe(IObserver<T> observer)
			{
				var e = eee.GetEnumerator();
				while (e.MoveNext())
					observer.OnNext(e.Current);
				observer.OnCompleted();
				return EmptyDisposable.Instance;
			}
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_defer_all<T, U> : f_primitive<IIdentList<T>, IIdentList<IIdentList<U>>>
		where T : IIdentity
		where U : IIdentity
	{
		public f_defer_all(IRtParent parent, IFunctor<T, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<T, U> g;

		protected sealed override IIdentList<IIdentList<U>> func(IIdentList<T> seq)
		{
			var arr = new IIdentList<U>[seq.Count];
			var ret = new IdentArray<IIdentList<U>>(seq, arr);

			int i = 0;
			var e = seq.GetEnumerator();
			while (e.MoveNext())
				arr[i++] = new IdentArray<U>(ret, new[] { g(e.Current) });

			return ret;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_apply_all<T, U> : f_primitive<IIdentList<T>, IIdentList<U>>
		where T : IIdentity
		where U : IIdentity
	{
		public f_apply_all(IRtParent parent, IFunctor<T, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<T, U> g;

		protected sealed override IIdentList<U> func(IIdentList<T> seq)
		{
			var ret = new IdentArray<U>(seq, seq.Count);

			int i = 0;
			var e = seq.GetEnumerator();
			while (e.MoveNext())
				ret[i++] = g(e.Current);

			return ret;
		}
	};

#if false
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	sealed class f_defer<T, U> : f_primitive<T, IAsyncTask<U>>
		where T : IIdentity
		where U : IIdentity
	{
		public f_defer(IRtParent parent, IFunctor<T, U> m)
			: base(prv)
		{
			if (IsDeferredType(m.GetType()))
				throw new Exception();

			this.rtg = prv;
			this.m = m;
			this.g = m.Func;
		}
		readonly IRtParent rtg;
		readonly IFunctor<T, U> m;
		readonly Func<T, U> g;

		protected sealed override IAsyncTask<U> func(T t)
		{
			return new AsyncTask<T, U>(this, t, t, g);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_take<U> : f_primitive<IIdentList<U>, IdentArray<U>>
		where U : IIdentity
	{
		public f_take(IRtParent parent, int n)
			: base(prv)
		{
			this.N = n;
		}
		public f_take(IRtParent parent)
			: this(prv, 1)
		{
		}
		public int N;

		protected override IdentArray<U> func(IIdentList<U> t)
		{
			int c = t.Count;
			if (c > N)
				c = N;
			var _new = new IdentArray<U>(t, c);
			var e = t.GetEnumerator();
			int i = 0;
			while (e.MoveNext() && i++ < c)
				_new[i] = e.Current;
			return _new;
		}
	};
#endif

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_spool<T, U> : f_primitive<IIdentList<T>, U>
		where T : IIdentity
		where U : IIdentity
	{
		public f_spool(IRtParent parent, IFunctor<IActiveObj<T>, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<IActiveObj<T>, U> g;

		protected sealed override U func(IIdentList<T> seq)
		{
			return g(new _seq_spooler(this, seq));
		}

		public class _seq_spooler : identity_node, IActiveObj<T>, IIdentList<T>
		{
			public _seq_spooler(IRtParent parent, IIdentList<T> seq)
				: base(parent, seq)
			{
				this.seq = seq;
			}
			protected readonly IIdentList<T> seq;
			public IDisposable Subscribe(IObserver<T> observer)
			{
				foreach (T t in seq)
					observer.OnNext(t);
				observer.OnCompleted();
				return EmptyDisposable.Instance;
			}
			public int Count { get { return seq.Count; } }
			public IEnumerator<T> GetEnumerator() { return seq.GetEnumerator(); }
			IEnumerator IEnumerable.GetEnumerator() { return seq.GetEnumerator(); }

			void ICollection.CopyTo(Array array, int index) { throw not.impl; }
			bool ICollection.IsSynchronized { get { throw not.impl; } }
			Object ICollection.SyncRoot { get { throw not.impl; } }

			public T this[int index] { get { return ((IIdentList<T>)seq)[index]; } }
		};
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_synchronize<T, U> : f_primitive<IActiveObj<T>, U>
		where T : IIdentity
	{
		public f_synchronize(IRtParent parent, IFunctor<IIdentList<T>, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<IIdentList<T>, U> g;

		protected sealed override U func(IActiveObj<T> src)
		{
			Debug.Assert(!(src is IEnumerable));
			return g(new IdentArray<T>(src, src.ToArrayWait()));
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_activate_obs<T> : f_primitive<IUnit, IActiveObj<T>>
		where T : IIdentity
	{
		public f_activate_obs(IRtParent parent, IActiveObj<T> hot_obs)
			: base(parent)
		{
			this.hot_obs = hot_obs;
		}
		readonly IActiveObj<T> hot_obs;

		protected override IActiveObj<T> func(IUnit _) { return hot_obs; }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class f_reduce<T, U> : f_primitive<IActiveObj<IActiveObj<T>>, IActiveObj<U>>
		where T : IIdentity
		where U : class, IIdentity
	{
		public f_reduce(IRtParent parent, IFunctor<IIdentList<T>, U> f)
			: base(parent)
		{
			this.g = f.Func;
		}
		readonly Func<IIdentList<T>, U> g;

		protected sealed override IActiveObj<U> func(IActiveObj<IActiveObj<T>> item)
		{
			return new _inner(this, item);
		}

		public sealed class _inner : m_observable<IActiveObj<IActiveObj<T>>, U>, IObserver<IActiveObj<T>>
		{
			public _inner(f_reduce<T, U> parent, IActiveObj<IActiveObj<T>> item)
				: base(parent, item)
			{
				this.g = parent.g;
			}

			readonly Func<IIdentList<T>, U> g;

			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			protected sealed override void start(IActiveObj<IActiveObj<T>> seq)
			{
				seq.Subscribe(this);
			}

			void IObserver<IActiveObj<T>>.OnNext(IActiveObj<T> value)
			{
				c_in++;
				_add_task();

				var arr = value.ToArrayWait();

				var idl = new IdentArray<T>(value, arr);

				U u = g(idl);

				_item(u);

				_remove_task();
			}

			void IObserver<IActiveObj<T>>.OnCompleted() { _remove_task(); }

			void IObserver<IActiveObj<T>>.OnError(Exception error) { _error(error); }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_fork<T, U> : f_primitive<T, IActiveObj<U>>
		where U : class, IIdentity
	{
		public f_fork(IRtParent parent, ForkJoin fj)
			: base(parent)
		{
			this.fj = fj;
		}
		readonly ForkJoin fj;

		protected sealed override IActiveObj<U> func(T item)
		{
			return new m_fold(this, item);
		}

		public sealed class m_fold : m_observable<T, U>, IObserver<U>
		{
			public m_fold(f_fork<T, U> parent, T item)
				: base(parent, item)
			{
				var arr = parent.fj.ItemsArr;
				this.rg = new IFunctor<T, IActiveObj<U>>[arr.Length];
				arr.CopyTo(rg, 0);
			}
			readonly IFunctor<T, IActiveObj<U>>[] rg;

			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			protected sealed override void start(T item)
			{
				foreach (var mm in rg)
				{
					_add_task();
					mm.Func(item).Subscribe(this);
				}
				_remove_task();
			}

			void IObserver<U>.OnNext(U value) { c_in++; _item(value); }
			void IObserver<U>.OnCompleted() { _remove_task(); }
			void IObserver<U>.OnError(Exception error) { _error(error); }

			public override String ToString()
			{
				return String.Format("join( >- {0})", typeof(U)._Name());
			}
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Merges an observable down to a scalar monad channel so that downstream monads process them together.
	/// Sends all 'T' from IFunctor(...,IActive(T)) to a single IMonad(T,U)  as IFunctor(...,IActive(U))
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_map<T, U> : f_primitive<IActiveObj<T>, IActiveObj<U>>
		where T : IIdentity
		where U : class, IIdentity
	{
		public f_map(IRtParent parent, IFunctor<T, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<T, U> g;

		protected sealed override IActiveObj<U> func(IActiveObj<T> src)
		{
			Debug.Assert(!(src is IEnumerable));
			return new _map(this, src);
		}

		public sealed class _map : m_observable<IActiveObj<T>, U>, IObserver<T>
		{
			public _map(f_map<T, U> parent, IActiveObj<T> src)
				: base(parent, src)
			{
				this.g = parent.g;
			}
			readonly Func<T, U> g;
			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			protected sealed override void start(IActiveObj<T> src)
			{
				src.Subscribe(this);
			}

			void IObserver<T>.OnNext(T value) { c_in++; _item(g(value)); }

			void IObserver<T>.OnCompleted() { _remove_task(); }

			void IObserver<T>.OnError(Exception ex) { _error(ex); }
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class f_activate_inner<T, U> : f_primitive<IActiveObj<IIdentList<T>>, IActiveObj<U>>
		where T : IIdentity
		where U : class, IIdentity
	{
		public f_activate_inner(IRtParent parent, IFunctor<IActiveObj<T>, U> f)
			: base(parent)
		{
			this.g = f.Func;
		}
		readonly Func<IActiveObj<T>, U> g;

		protected sealed override IActiveObj<U> func(IActiveObj<IIdentList<T>> item)
		{
			return new _inner(this, item);
		}

		public sealed class _inner : m_observable<IActiveObj<IIdentList<T>>, U>, IObserver<IIdentList<T>>
		{
			public _inner(f_activate_inner<T, U> parent, IActiveObj<IIdentList<T>> item)
				: base(parent, item)
			{
				this.g = parent.g;
			}

			readonly Func<IActiveObj<T>, U> g;
			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			protected sealed override void start(IActiveObj<IIdentList<T>> seq)
			{
				seq.Subscribe(this);
			}

			void IObserver<IIdentList<T>>.OnNext(IIdentList<T> value)
			{
				c_in++;

				var act = new f_activate_seq<T>._spooler(this, value);

				U u = g(act);

				_item(u);
			}

			void IObserver<IIdentList<T>>.OnCompleted() { _remove_task(); }

			void IObserver<IIdentList<T>>.OnError(Exception error) { _error(error); }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// apply Func(T,U) to every item in a series of observably-delivered collections, sending one array of U's for
	/// each on the fly.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_switch<T, U> : f_primitive<IActiveObj<IIdentList<T>>, IActiveObj<IdentArray<U>>>
		where T : IIdentity
		where U : IIdentity
	{
		public f_switch(IRtParent parent, IFunctor<T, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<T, U> g;

		protected sealed override IActiveObj<IdentArray<U>> func(IActiveObj<IIdentList<T>> src)
		{
			Debug.Assert(!(src is IEnumerable));
			return new _switch(this, src);
		}

		public sealed class _switch : _monad<IActiveObj<IIdentList<T>>>, IObserver<IIdentList<T>>, IActiveObj<IdentArray<U>>, IDisposable
		{
			public _switch(f_switch<T, U> parent, IActiveObj<IIdentList<T>> src)
				: base(parent, src)
			{
				this.g = parent.g;
			}
			readonly Func<T, U> g;
			IObserver<IdentArray<U>> obs;
			IDisposable d;
			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			public IDisposable Subscribe(IObserver<IdentArray<U>> obs)
			{
				Debug.Assert(this.obs == null);

				this.obs = obs;
				this.d = t0.Subscribe(this);
				return this;
			}

			void IObserver<IIdentList<T>>.OnNext(IIdentList<T> value)
			{
				c_in++;
				var _tmp = obs;
				if (_tmp != null)
				{
					U[] rgu = new U[value.Count];
					var e = value.GetEnumerator();
					for (int i = 0; e.MoveNext() && i < rgu.Length; i++)
						rgu[i] = g(e.Current);

					_tmp.OnNext(new IdentArray<U>(value, rgu));
				}
			}

			void IObserver<IIdentList<T>>.OnCompleted()
			{
				var _tmp = Interlocked.Exchange(ref obs, null);
				if (_tmp != null)
					_tmp.OnCompleted();
			}

			void IObserver<IIdentList<T>>.OnError(Exception ex)
			{
				var _tmp = Interlocked.Exchange(ref obs, null);
				if (_tmp == null)
					throw ex;
				else
					_tmp.OnError(ex);
			}

			public void Dispose()
			{
				var _tmp = Interlocked.Exchange(ref d, null);
				if (_tmp != null)
					_tmp.Dispose();
			}
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_project<T, U> : f_primitive<IActiveObj<T>, IActiveObj<IActiveObj<U>>>
		where T : IIdentity
		where U : IIdentity
	{
		public f_project(IRtParent parent, IFunctor<T, U> m)
			: base(parent)
		{
			this.g = m.Func;
		}
		readonly Func<T, U> g;

		protected sealed override IActiveObj<IActiveObj<U>> func(IActiveObj<T> src)
		{
			//Debug.Assert(!(src is IEnumerable));
			return new _select(this, src);
		}

		public sealed class _select : _monad<IActiveObj<T>>, IObserver<T>, IActiveObj<IActiveObj<U>>, IDisposable
		{
			public _select(f_project<T, U> parent, IActiveObj<T> src)
				: base(parent, src)
			{
				this.g = parent.g;
			}
			readonly Func<T, U> g;
			IObserver<IActiveObj<U>> obs;
			IDisposable d;
			int c_in;
			public override int InputItemsCount { get { return c_in; } }

			public IDisposable Subscribe(IObserver<IActiveObj<U>> obs)
			{
				Debug.Assert(this.obs == null);

				this.obs = obs;
				this.d = t0.Subscribe(this);
				return this;
			}

			void IObserver<T>.OnNext(T value)
			{
				c_in++;
				var _tmp = obs;
				if (_tmp != null)
					_tmp.OnNext(new f_return<U>(this, g(value)));
			}

			void IObserver<T>.OnCompleted()
			{
				var _tmp = Interlocked.Exchange(ref obs, null);
				if (_tmp != null)
					_tmp.OnCompleted();
			}

			void IObserver<T>.OnError(Exception ex)
			{
				var _tmp = Interlocked.Exchange(ref obs, null);
				if (_tmp == null)
					throw ex;
				else
					_tmp.OnError(ex);
			}

			public void Dispose()
			{
				var _tmp = Interlocked.Exchange(ref d, null);
				if (_tmp != null)
					_tmp.Dispose();
			}
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// provided for cases like re-binding where a functor must be specified. because otherwise you could just 
	/// not bind this functor.
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class f_identity<U> : f_primitive<U, U>
	{
		public static readonly f_identity<U> Instance = new f_identity<U>(SysObj.Instance);

		public f_identity(IRtParent parent)
			: base(parent)
		{
		}

		protected sealed override U func(U item)
		{
			Debug.Print("identity: {0}", item.GetType()._Name());
			return item;
		}
	};

	public sealed class f_identity : f_identity<ISchemaObj>
	{
		public f_identity(IRtParent parent)
			: base(parent)
		{
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_materialize : f_primitive<IIdentity, IIdentity>
	{
		public f_materialize(IRtParent parent)
			: base(parent)
		{
		}

		protected sealed override IIdentity func(IIdentity item)
		{
			//using (new _indent("f_materialize {0}", item.GetType()._Name()))
			{
				var u = item as IItems<ISchemaObj>;
				if (u == null)
					return item;

				var c1 = item as IIdentList<ISchemaObj>;
				if (c1 != null)
					return c1;

				var arr = ((IActiveObj<IIdentity>)item).ToArrayWait();
				return new IdentArray<IIdentity>(item, arr);
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_first<T, U> : f_primitive<T, U>
	{
		public f_first(IRtParent parent, IFunctor<T, IItems<U>> g)
			: base(parent)
		{
			this.g = g;
		}
		readonly IFunctor<T, IItems<U>> g;

		protected override U func(T item)
		{
			var seq = g.Func(item);

			var s1 = seq as IReadOnlyList<U>;
			if (s1 != null)
				return s1.Count == 0 ? default(U) : s1[0];

			var s2 = seq as IEnumerable<U>;
			if (s2 != null)
			{
				var e = s2.GetEnumerator();
				if (!e.MoveNext())
					return default(U);
				return e.Current;
			}

			var s3 = seq as IObservable<U>;
			if (s3 != null)
			{
				var arr = s3.ToArrayWait();
				return arr.Length == 0 ? default(U) : arr[0];		// fix fix
			}

			throw new Exception("not a sequence");
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_map_selected<U, T> : f_primitive<T, T>
		where U : class, T
	{
		public f_map_selected(IRtParent parent, IFunctor<U, T> g)
			: base(parent)
		{
			this.Functor = g ?? f_fatal<U, T>.Instance;
		}
		MethodInfo mi;
		Object tgt;
		IFunctor<U, T> g;

		public IFunctor<U, T> Functor
		{
			get { return g; }
			set
			{
				this.mi = (this.g = value).Func.Method;
				this.tgt = g.Func.Target;
			}
		}

		protected sealed override T func(T item)
		{
			//using (new _indent("f_expand {0}", item.GetType()._Name()))
			{
				U selected;
				return (selected = item as U) == null ? item : (T)mi.Invoke(tgt, 0, null, new Object[] { selected }, null);
			}
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class f_map_collection<T, U> : f_primitive<IIdentList<T>, IIdentList<U>>
	{
		public f_map_collection(IRtParent parent, IFunctor<T, U> g)
			: base(parent)
		{
			this.Functor = g ?? f_fatal<T, U>.Instance;
		}
		MethodInfo mi;
		Object tgt;
		IFunctor<T, U> g;

		public IFunctor<T, U> Functor
		{
			get { return g; }
			set
			{
				this.mi = (this.g = value).Func.Method;
				this.tgt = g.Func.Target;
			}
		}

		protected sealed override IIdentList<U> func(IIdentList<T> coll)
		{
			var arr = new U[coll.Count];
			int i = 0;
			foreach (var item in coll)
				arr[i++] = (U)mi.Invoke(tgt, 0, null, new Object[] { item }, null);

			return new IdentArray<U>(coll, arr);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// ( ...this is a portion of the class below... )
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class f_compose : f_primitive<IEnumerable<IIdentity>, IFunctor>
	{
		public static readonly IFunctor<IEnumerable<IIdentity>, IFunctor> Instance;

		static f_compose()
		{
			Instance = new f_compose(SysObj.Instance);
		}

		public static bool IsDeferredType(Type T)
		{
			return
				typeof(IObservable<IIdentity>).IsAssignableFrom(T) ||
				typeof(IAsyncResult).IsAssignableFrom(T) ||
				typeof(Task).IsAssignableFrom(T);
		}
		public static bool IsDeferredItem(IIdentity item)
		{
			return IsDeferredType(item.GetType());
		}
		public static Type GetItemsArgType(Type t)
		{
			Type v;
			return (v = t.FindGenericInterface(typeof(IItems<>))) == null ? null : v.GetGenericArguments()[0];
		}
		public static Type GetEnumerableArgType(Type t)
		{
			Type v;
			return (v = t.FindGenericInterface(typeof(IEnumerable<>))) == null ? null : v.GetGenericArguments()[0];
		}
		public static Type GetIdentCollArgType(Type t)
		{
			Type v;
			return (v = t.FindGenericInterface(typeof(IIdentList<>))) == null ? null : v.GetGenericArguments()[0];
		}
		public static Type GetIdentListArgType(Type t)
		{
			Type v;
			return (v = t.FindGenericInterface(typeof(IIdentList<>))) == null ? null : v.GetGenericArguments()[0];
		}
		public static Type GetActiveObjArgType(Type t)
		{
			Type v;
			return (v = t.FindGenericInterface(typeof(IActiveObj<>))) == null ? null : v.GetGenericArguments()[0];
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// bind the specified functors sequentially, subject to an analysis of input-to-output type matching. For incompatible
	/// boundaries, try to insert the appropriate type-adapters. Input is list of IIdentity (as opposed to IFunctor) to
	/// accomodate the first element not being a functor, if desired (e.g. materialized collections can be used. In this 
	/// case, the input to the composed functor will be IUnit).
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class f_compose : f_primitive<IEnumerable<IIdentity>, IFunctor>
	{
		public f_compose(IRtParent parent)
			: base(parent)
		{
		}

		IFunctor try_upgrade(IIdentity cur)
		{
			IFunctor f;
			if ((f = cur as IFunctor) != null)
				return f;

			Type Titem, Tseq = cur.GetType();

			if ((Titem = GetIdentCollArgType(Tseq)) != null)
				return typeof(f_activate_seq<>).MakeGenericType(Titem)._ctor<IFunctor>(this, cur);

			if ((Titem = GetActiveObjArgType(Tseq)) != null)
				return typeof(f_activate_obs<>).MakeGenericType(Titem)._ctor<IFunctor>(this, cur);

			return null;
		}

		protected sealed override IFunctor func(IEnumerable<IIdentity> seq)
		{
			IEnumerator<IIdentity> e;
			if (!(e = seq.GetEnumerator()).MoveNext())
				return f_identity.Instance;

			IFunctor f;
			if ((f = try_upgrade(e.Current)) == null)
				throw new Exception();

			return compose(e, f);
		}

		/// <summary>
		/// Compose the list via function application ('bind') sequentially until a fanout is needed. At that point, 
		/// recurse to compose the remainder of the list, apply an adapter the the result, and bind to that.
		/// </summary>
		public static IFunctor compose(IEnumerator<IIdentity> e, IFunctor prv)
		{
			while (e.MoveNext())
			{
				var cur = (IFunctor)e.Current;

				//if (nxt is SaveToNewWorkspace)
				//	nxt = ((SaveToNewWorkspace)nxt).Func(prv);

				Type T = prv.Targs[1], U = cur.Targs[0], Tm, Tseq, Tobs, Tnst;

				if (!U.IsAssignableFrom(T))
				{
					if ((Tseq = GetIdentCollArgType(T)) != null && U.IsAssignableFrom(Tseq))
					{
						Tm = typeof(f_apply_all<,>).MakeGenericType(Tseq, cur.Targs[1]);
					}
					else if ((Tobs = GetActiveObjArgType(T)) != null && U.IsAssignableFrom(Tobs))
					{
						Tm = typeof(f_map<,>).MakeGenericType(Tobs, cur.Targs[1]);
					}
					else
					{
						/// for fan-out, bind to the recursive composition of the remainder of the list

						cur = compose(e, cur);

#if false
						else if (Tobs != null && U.IsAssignableFrom(Tobs))
						{
							Tm = typeof(f_project<,>).MakeGenericType(cur.Targs);
						}
#endif
						/* else */ if (Tseq != null && U.IsAssignableFrom(typeof(IActiveObj<>).MakeGenericType(Tseq)))
						{
							Debug.Print("Map '{0}' is not necessary?", T._Name());
							//Nop.CodeCoverage(IsDeferredType(U));

							Tm = typeof(f_spool<,>).MakeGenericType(Tseq, cur.Targs[1]);
						}
						else if (Tobs != null && U.IsAssignableFrom(typeof(IIdentList<>).MakeGenericType(Tobs)))
						{
							Nop.CodeCoverage(IsDeferredType(U));

							Tm = typeof(f_synchronize<,>).MakeGenericType(Tobs, cur.Targs[1]);
						}
						else if (Tobs != null && (Tnst = GetIdentCollArgType(Tobs)) != null && U.IsAssignableFrom(Tnst))
						{
							Nop.CodeCoverage(IsDeferredType(U));

							Tm = typeof(f_switch<,>).MakeGenericType(Tnst, cur.Targs[1]);
						}
						else if (Tobs != null && (Tnst = GetActiveObjArgType(Tobs)) != null && U.IsAssignableFrom(typeof(IIdentList<>).MakeGenericType(Tnst)))
						{
							Tm = typeof(f_reduce<,>).MakeGenericType(Tnst, cur.Targs[1]);
						}
						else if (Tobs != null && (Tnst = GetIdentCollArgType(Tobs)) != null && U.IsAssignableFrom(typeof(IActiveObj<>).MakeGenericType(Tnst)))
						{
							Tm = typeof(f_activate_inner<,>).MakeGenericType(Tnst, cur.Targs[1]);
						}
						else
						{
							throw not.impl;
						}
					}

					cur = Tm._ctor<IFunctor>(prv, cur);
				}

				prv = cur.Bind(prv);
			}
			return prv;
		}
	};
}
